package org.fing.edu.uy.esbadp.motor.register;

import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;

import org.fing.edu.uy.esbadp.core.entity.DMAdpAdaptationRequirement;
import org.fing.edu.uy.esbadp.core.entity.DMAdpAdaptationRequirements;
import org.fing.edu.uy.esbadp.core.entity.DMAdpEvents;
import org.fing.edu.uy.esbadp.core.entity.DMAdpStrategies;
import org.fing.edu.uy.esbadp.core.entity.DMAdpStrategy;
import org.fing.edu.uy.esbadp.core.interfaces.IAdpStrategy;
import org.fing.edu.uy.esbadp.logger.LoggerADP;

@SuppressWarnings("rawtypes")
public class RegistryManager {
	
	private static java.util.logging.Logger logger = LoggerADP.getLogger(RegistryManager.class.getName());
	
	private static RegistryManager instance;
	
	private DMAdpEvents adpEvents;
	
	private DMAdpAdaptationRequirements adpRequirements;
	
	private DMAdpStrategies adpStrategies;
	
	private RegistryManager(){};
	
	public synchronized static RegistryManager getInstance(){
		if(instance == null){
			instance = new RegistryManager();
		}
		return instance;
	}

	public void setAdpEvents(DMAdpEvents adpEvents) {
		this.adpEvents = adpEvents;
	}
	
	public DMAdpEvents getAdpEvents() {
		return adpEvents;
	}

	public void setAdpStrategies(DMAdpStrategies adpStrategies) {
		this.adpStrategies = adpStrategies;
	}
	
	public DMAdpStrategies getAdpStrategies() {
		return adpStrategies;
	}
	
	public void setAdpRequirements(DMAdpAdaptationRequirements adpRequirements) {
		this.adpRequirements = adpRequirements;
	}
	
	public DMAdpAdaptationRequirements getAdpRequirements() {
		return adpRequirements;
	}
	
	/**
	 * @param eventName identificador de evento de monitoreo. 
	 * @return retorna el primer requrimeinto que es disparado a partir del nombre del evento pasado por parametro,
	 * en caso de no existir ninguni retorna null.
	 */
	public DMAdpAdaptationRequirement getAdpRequirementForEvent(String eventName){		
		for(DMAdpAdaptationRequirement aux : this.adpRequirements.getRequeriments()){
			if(aux.getEvent_triggered().equals(eventName)){
				return aux;
			}
		}		
		return null;
	}

	/**
	 * @param requirementName identificador de un requerimiento de adaptacion 
	 * @return retorna una lista con todas las estrategias que soportan el requerimiento dado , lo cual no indica que para el caso particular 
	 * puedan generar una directiva de adaptacion, eso dependera de los valores monitorizados.
	 */
	public List<DMAdpStrategy> getAdpStrategiesForRequirement(String requirementName){		
		List<DMAdpStrategy> ret = new LinkedList<DMAdpStrategy>();
		//busco cada una de las estrategias cargadas las que soportan el requerimiento
		for(DMAdpStrategy aux : this.adpStrategies.getStrategies()){
			if(aux.getSupported_requirement().getNames().contains(requirementName)){
				ret.add(aux);
			}
		}		
		return ret;
	}
	
	/***
	 * 
	 * @param strategy indentificador de la estragia
	 * @return nueva instancia de la estrategia , null en si no se puede crear la instancia.
	 */
	public IAdpStrategy getAdpStrategyInstance(String strategyName){
		DMAdpStrategy strategy = null;
		for(DMAdpStrategy aux : this.adpStrategies.getStrategies()){
			if(aux.getName().equals(strategyName)){
				strategy = aux;
				break;
			}
		}
		Class klass;
		try {
			klass = Class.forName(strategy.getClassName());
			IAdpStrategy adpImplementation = (IAdpStrategy) klass.newInstance();				
			return adpImplementation;
		} catch (Exception ex) {
			logger.log(Level.SEVERE, "Error creating new instance of " + strategy.getClassName(), ex);
			return null;
		} 			
	}
}
